//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef __AS_H__
#define __AS_H__

class AS;
class VSegment;
class StdSegment;
class VDataSource;

class AS
{
public:
    ECode Initialize();
    void Activate();

    Boolean IsValidAddress(const UInt32 uAddr);

    ECode Map(
            UInt32 uAddr,
            UInt32 uSize,
            UInt32 dwFlags,
            UInt32 dwProtect,
            VDataSource * pDataSource,
            UInt32 uOffset,
            UInt32 *puAddr);

    ECode Alloc(
            UInt32 uAddr,
            UInt32 uSize,
            UInt32 dwFlags,
            UInt32 dwProtect,
            UInt32 *puAddr)
    {
        return Map(uAddr, uSize, dwFlags, dwProtect, NULL, 0, puAddr);
    }

    ECode AllocThreadStack(
            UInt32 *puStackBase);

    ECode DecreaseThreadStack(
            UInt32 uStackBase);

    ECode Remap(
            UInt32 uVAddr,
            UInt32 uSize,
            UInt32 uPAddr,
            UInt32 dwFlags,
            UInt32 dwProtect,
            UInt32 *puAddr);

    ECode Query(
            UInt32 uAddr,
            UInt32 *puBaseAddr,
            MemorySize *pSize);

    ECode QueryPhysicalBlock(
            UInt32 uVAddr,
            UInt32 *puPAddr,
            MemorySize *pSize);

    ECode Unmap(
            UInt32 uAddr,
            UInt32 uSize);

    ECode SetProtect(
            UInt32 uAddr,
            UInt32 uSize,
            UInt32 dwProtect);

    ECode CheckProtect(
            UInt32 uAddr,
            UInt32 protect,
            Boolean *pIsValid);

    ECode IsAutoCommit(
            UInt32 uAddr,
            Boolean *pIsAutoCommit);

    ECode Hit(UInt32 vAddr);

    ECode Break(UInt32 vAddr);

    ECode GetMemorySizes(
            Int32 *pPrivateVirtualMemorySize,
            Int32 *pSharedVirtualMemorySize,
            Int32 *pPrivatePhysicalMemorySize,
            Int32 *pSharedPhysicalMemorySize);

#ifndef _RELEASE
    void View();
#endif // _RELEASE
    AS() : m_pHat(NULL)
#ifdef DEBUG_KMUTEX
        , m_asLock(__FILE__, __LINE__)
#endif //DEBUG_KMUTEX
	{ }
    ~AS();

public:
    DLinkNode   m_commList;     // global address segments list
    VSegment    *m_pCommHint;

    DLinkNode   m_privList;     // private segments list
    VSegment    *m_pPrivHint;

    VSegment    *m_pHeapSeg;    // heap segment(the first seg of m_privList)
    HAT         *m_pHat;

    KMutex      m_asLock;       // lock for whole AS object
};

inline ECode AS::Initialize()
{
    m_pHat = new HAT;
    if (!m_pHat) {
        assert(0 && "Not enough physical memory");
        return E_OUT_OF_MEMORY;
    }

    m_commList.Initialize();
    m_privList.Initialize();
    m_pCommHint = m_pPrivHint = m_pHeapSeg = NULL;
    return NOERROR;
}

inline void AS::Activate()
{
    assert(m_pHat != NULL);
    m_pHat->Activate();
}

class VSegment : public DLinkNode
{
public:
    UInt32          m_uBase;
    UInt32          m_uSize;
    AS *            m_pOwner;

    VSegment(AS * pOwner, UInt32 uBase, UInt32 uSize) :
        m_uBase(uBase),
        m_uSize(uSize),
        m_pOwner(pOwner)
    {
    }

public:
    virtual ECode InitMap() = 0;

    virtual ECode CloneMemBlockForProcess(UInt32 uAddr, UInt32 uSize) = 0;

    virtual ECode SetProtect(UInt32 dwProtect) = 0;

    virtual Boolean CheckProtect(UInt32 protect) = 0;

    virtual Boolean IsAutoCommit() = 0;

    virtual ECode Hit(UInt32 uAddr) = 0;

    virtual ECode Unmap() = 0;

    virtual ECode Decrease(UInt32 uAddr) = 0;

    virtual UInt32 GetPhyscialMemorySize() = 0;

    virtual ECode QueryPhysicalBlock(
            UInt32 uVAddr, UInt32 *puPAddr, MemorySize *pSize) = 0;
};

class StdSegment : public VSegment
{
public:
    ECode InitMap();

    ECode CloneMemBlockForProcess(UInt32 uAddr, UInt32 uSize);

    ECode SetProtect(UInt32 dwProtect);

    Boolean CheckProtect(UInt32 protect);

    Boolean IsAutoCommit();

    ECode Hit(UInt32 uAddr);

    ECode Unmap();

    ECode Decrease(UInt32 uAddr);

    UInt32 GetPhyscialMemorySize();

    ECode QueryPhysicalBlock(
            UInt32 uVAddr, UInt32 *puPAddr, MemorySize *pSize);

    StdSegment(
        AS * pOwner, UInt32 uAddr, UInt32 uSize,
        UInt32 dwFlags, UInt32 dwProtect, VDataSource * pDataSource,
        UInt32 uOffset) :
            VSegment(pOwner, uAddr, uSize),
            m_dwFlags(dwFlags),
            m_dwProtect(dwProtect),
            m_pDataSource(pDataSource),
            m_uOffset(uOffset),
            m_pPrivCache(NULL)
    {
    }

    virtual ~StdSegment();

private:
    ECode MapCache(PageContainer * pCache, UInt32 uMapBase, UInt32 uMapSize);

private:
    UInt32           m_dwFlags;
    UInt32           m_dwProtect;
    VDataSource *   m_pDataSource;
    UInt32          m_uOffset;

    PageContainer * m_pPrivCache;
};

class VDataSource
{
public:
    virtual PPage * GetPage(UInt32 uOffset) = 0;
    virtual ECode Flush() = 0;

    virtual ~VDataSource() {}

public:
    PageContainer  m_cache;
};

#define ASDBG_NORMAL        1
#define ASDBG_WARNING       2
#define ASDBG_ERROR         3
#define ASDBG_NONE          100
#define ASDBG_LEVEL         ASDBG_WARNING

#if defined(_DEBUG) || defined(_AS_DEBUG)
#define AS_DBGOUT(level, exp) \
    ((level) >= ASDBG_LEVEL ? (void)(exp) : (void)0)
#else
#define AS_DBGOUT(level, exp)
#endif // _AS_DEBUG

#endif // __AS_H__
